package com.xbell.app.sys.dao;

import java.io.IOException;
import java.io.Reader;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.sql.Clob;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;

import javax.persistence.EntityManager;

import org.hibernate.Session;
import org.hibernate.query.internal.NativeQueryImpl;
import org.hibernate.transform.Transformers;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

import com.xbell.app.sys.common.GridDataSet;
import com.xbell.app.sys.common.PageInfo;
import com.xbell.app.sys.common.ResponseCode;
import com.xbell.app.sys.common.Trans;
import com.xbell.app.sys.common.XbellDynamicStringUtil;
import com.xbell.app.sys.common.XbellRuntimeException;

@Repository
public  class AbstractBaseDao implements AbstractBaseDaoI {

	@Autowired
	protected EntityManager em;

	@Override
	public org.hibernate.query.Query createNativeQuery(String sql) {

		return em.createNativeQuery(sql).unwrap(NativeQueryImpl.class);
	}

	@Override
	public int getNumberSql(String sql, Map<String, Object> params) {
		org.hibernate.query.Query q = this.em.unwrap(Session.class).createSQLQuery(sql);
		if(params!=null&&params.size()>0)
			q.setProperties(params);
		return ((BigDecimal)q.getSingleResult()).intValue();
	}

	@Override
	public int countQuery(String sql, Map<String, Object> params) {
		return this.getNumberSql("select count(*) from ("+sql+")", params);
	}

	@Override
	public <T> List<T> queryEntityList(String sql, Map<String, Object> params) {
		org.hibernate.query.Query q = em.unwrap(Session.class).createQuery(sql);
		if(params!=null&&params.size()>0)
			q.setProperties(params);
		return (List<T>)q.list();
	}

	@Override
	public List<Map<String, Object>> queryList(String sql, Map<String, Object> params) {
		org.hibernate.query.Query q = em.createNativeQuery(sql).unwrap(NativeQueryImpl.class).setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
		if(params!=null&&params.size()>0)
			q.setProperties(params);
		return Trans.transformLowerCase((List<Map<String,Object>>)q.list());
	}

	@Override
	public <T> T queryEntity(String sql, Map<String, Object> params) {
		org.hibernate.query.Query q = em.unwrap(Session.class).createQuery(sql);
		if(params!=null&&params.size()>0)
			q.setProperties(params);
		return (T)q.uniqueResult();
	}
	
	@Override
	public <T> T queryEntity(String sql,String key,Object value,Object... params)
	{
		Map<String,Object> param = XbellDynamicStringUtil.addParamIfNotBlank(key,value);
		XbellDynamicStringUtil.addParamIfNotBlank(param,params);
		return this.queryEntity(sql, param);
	}

	@Override
	public Map<String, Object> queryMap(String sql, Map<String, Object> params) {
		org.hibernate.query.Query q = em.createNativeQuery(sql).unwrap(NativeQueryImpl.class).setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
		if(params!=null&&params.size()>0)
			q.setProperties(params);
		return Trans.transformLowerCase((Map<String,Object>)q.uniqueResult());
	}
	
	@Override
	public Map<String, Object> queryMap(String sql,String key,Object value,Object... params)
	{
		Map<String,Object> param = XbellDynamicStringUtil.addParamIfNotBlank(key,value);
		XbellDynamicStringUtil.addParamIfNotBlank(param,params);
		return this.queryMap(sql, param);
	}
	
	@Override
	public <T> T queryUnique(String sql,String key,Object value,Object... params)
	{
		Map<String,Object> param = XbellDynamicStringUtil.addParamIfNotBlank(key,value);
		XbellDynamicStringUtil.addParamIfNotBlank(param,params);
		return this.queryUnique(sql, param);
	}
	public <T> T queryUnique(String sql,Map<String, Object> params)
	{
		org.hibernate.query.Query q = em.createNativeQuery(sql).unwrap(NativeQueryImpl.class).setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
		if(params!=null&&params.size()>0)
			q.setProperties(params);
		return (T)q.uniqueResult();
	}

	@Override
	public int execUpdateSql(String sql, Map<String, Object> params) {
		org.hibernate.query.Query q =  em.unwrap(Session.class).createSQLQuery(sql);
		if(params!=null&&params.size()>0)
			q.setProperties(params);
		return q.executeUpdate();
	}
	
	public int execUpdateSql(String sql,String key,Object value,Object... params)
	{
		Map<String,Object> param = XbellDynamicStringUtil.addParamIfNotBlank(key,value);
		XbellDynamicStringUtil.addParamIfNotBlank(param,params);
		return this.execUpdateSql(sql, param);
	}

	@Override
	public <T> Object saveEntity(T t) {
		 return em.unwrap(Session.class).save(t);
	}

	@Override
	public <T> void updateEntity(T t) {
		em.unwrap(Session.class).update(t);
	}

	@Override
	public <T> void delEntity(T t) {
		em.unwrap(Session.class).delete(t);
	}

	@Override
	public void clear() {
		em.clear();
	}

	@Override
	public void flush() {
		em.flush();
	}
	
	@Override
	public void detach(Object entity)
	{
		this.em.detach(entity);
	}
	@Override
	public void clearBySession() {
		em.unwrap(Session.class).clear();
	}

	@Override
	public void flushBySession() {
		em.unwrap(Session.class).flush();
	}
	
	public EntityManager getEntityManager()
	{
		return this.em;
	}

	@Override
	public GridDataSet fastPageSql(String sql, PageInfo page, Map<String,Object> params)
	{
		GridDataSet dg = new GridDataSet();
		if (!page.isAll()) {
			int records = this.countQuery(sql, params);
			if(records==0) {
				return GridDataSet.emptyGrid;
			}
			dg.setRecords(records);
			dg.setTotal((int)Math.ceil(records*1.0f/page.getRows()));
		}
		org.hibernate.query.Query q = em.createNativeQuery(("select * from (" + sql + ")") + page.orderSql()).unwrap(NativeQueryImpl.class).setProperties(params).setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
		if (!page.isAll()) {
			q.setFirstResult((page.getPage()-1)*page.getRows());
			q.setMaxResults(page.getRows());
		}
		dg.setRowdata(Trans.transformLowerCase(q.list()));
		return dg;
	}

	@Override
	public GridDataSet fastPageSqlWithLowercase(String sql,PageInfo page,Map<String,Object> params)
	{
		GridDataSet dg = this.fastPageSql(sql, page, params);
		dg.setRowdata(Trans.transformLowerCase((List<Map<String,Object>>)dg.getRowData()));
		return dg;
	}
	@Override
	public GridDataSet fastPageSql(String sql,PageInfo page,String key,Object value,Object ...args)
	{
		Map<String,Object> param = XbellDynamicStringUtil.addParamIfNotBlank(key,value);
		XbellDynamicStringUtil.addParamIfNotBlank(param,args);
		return this.fastPageSql(sql, page, param);
	}
	@Override
	public GridDataSet fastPageSqlWithLowercase(String sql, PageInfo page, String key, Object value, Object ...args)
	{
		Map<String,Object> param = XbellDynamicStringUtil.addParamIfNotBlank(key,value);
		XbellDynamicStringUtil.addParamIfNotBlank(param,args);
		return this.fastPageSqlWithLowercase(sql, page, param);
	}

	@Override
	public <T> Map<String,T> queryMapToMap(String sql,String mapKey,String mapValue,Map<String,Object> params)
	{
		List<Map<String,Object>> qres = this.queryList(sql, params); /// 查询结果
		Map<String,T> ObjectRes = new HashMap<String,T>();
		for(Map<String,Object> item : qres) {
			if(item.get(mapKey)==null)continue;
			String kk = item.get(mapKey).toString();
			if(mapValue==null) {
				ObjectRes.put(kk, (T)item);
			}
			else
			{
				Object val = item.get(mapValue);
				if(val==null)continue;
				ObjectRes.put(kk, (T)val);
			}
		}
		return ObjectRes;
	}

	@Override
	public <T> Map<String,T> queryMapToMap(String sql,String mapKey,String mapValue,String key,Object value,Object... params)
	{
		Map<String,Object> param = XbellDynamicStringUtil.addParamIfNotBlank(key,value);
		XbellDynamicStringUtil.addParamIfNotBlank(param,params);
		return this.queryMapToMap(sql, mapKey, mapValue, param);
	}

	@Override
	public <T,V> Map<String,T> queryEntityToMap(String sql,String keyField,String valueField,Map<String,Object> params)
	{
		List<V> qres = this.queryEntityList(sql, params); /// 查询结果
		Map<String,T> ObjectRes = new HashMap<String,T>();
		try {
			for(V item : qres) {
				/// 获取实体类的属性
				Field fd = item.getClass().getDeclaredField(keyField);
				fd.setAccessible(true);
				Object kkObj = fd.get(item);
				if(kkObj==null)continue;
				String kk = kkObj.toString();
				if(valueField==null) {
					ObjectRes.put(kk, (T)item);
				}
				else
				{
					fd = item.getClass().getField(valueField);
					fd.setAccessible(true);
					Object val = fd.get(item);
					if(val==null)continue;
					ObjectRes.put(kk, (T)val);
				}
			}
		} catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) {
			e.printStackTrace();
			throw new XbellRuntimeException("转化失败", ResponseCode.ResponseCodeInnerError);
		}
		return ObjectRes;
	}

	@Override
	public <T,V> Map<String,T> queryEntityToMap(String sql,String keyField,String valueField,String key,Object value,Object... params)
	{
		Map<String,Object> param = XbellDynamicStringUtil.addParamIfNotBlank(key,value);
		XbellDynamicStringUtil.addParamIfNotBlank(param,params);
		return this.queryEntityToMap(sql, keyField, valueField, param);
	}
	
	@Override
	public BigDecimal transBigDecimal(Object obj)
	{
		return ((BigDecimal)obj);
	}
	
	@Override
	public String transClobToString(Object obj)
	{
		if(obj==null)return null;
		if(obj.getClass().isAssignableFrom(String.class)) {
			return (String)obj;
		}
			Clob clob = (Clob)obj;
			try(Reader ris = clob.getCharacterStream()){
				char[] buffer = new char[(int)clob.length()];
				ris.read(buffer); 
				return String.valueOf(buffer);
			}catch (SQLException sex) {
				sex.printStackTrace();
			} catch (IOException ioex) {
				ioex.printStackTrace();
			}
		return null;
	}
	
	/**
	 * 格式化sql,XbellDynamicStringUtil快捷调用
	 * @see XbellDynamicStringUtil#format(String, Map, Class, String)
	 * @param <T>
	 * @param sql 
	 * @param params
	 * @param clz
	 * @param id
	 * @return
	 * @datetime 2023年8月14日 下午2:59:35
	 * @author 王文胜
	 */
	public <T> String formatSql(String sql,Map<String,Object> params,Class<T> clz,final String id) {
		return XbellDynamicStringUtil.format(sql, params, clz, id);
	}
	public int addParamIfNotBlank(Map<String,Object> map,Object ...objs)
	{
		return XbellDynamicStringUtil.addParamIfNotBlank(map, objs);
	}
	public int addLikeParamIfNotBlank(Map<String,Object> map,Object ... objs)
	{
		return XbellDynamicStringUtil.addLikeParamIfNotBlank(map, objs);
	}
	
	public Map<String,Object> addParamIfNotBlank(Object ...objs)
	{
		return XbellDynamicStringUtil.addParamIfNotBlank(objs);
	}
	
	/**
	 * 获取实体类
	 * @param <T>
	 * @param cls 实体类
	 * @param pk 主键
	 * @return
	 */
	@Override
	public <T> Optional<T> find(Class<T> cls,Object pk) {
		T t = this.em.find(cls, pk);
		return Optional.ofNullable(t);
	}
	
}
